package com.android.camera2.host.mode;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.MeteringRectangle;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.util.Log;
import android.util.Range;
import android.util.Rational;
import android.util.Size;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.View;
import android.widget.SeekBar;
import androidx.annotation.NonNull;
import com.android.camera2.R;
import com.android.camera2.common.ui.MySeekBar;
import com.android.camera2.common.utils.CameraParameter;
import com.android.camera2.common.utils.CameraUtils;
import com.android.camera2.common.utils.LogUtil;
import com.android.camera2.common.app.IApp;
import com.android.camera2.common.mode.ICameraMode;
import com.android.camera2.common.ui.IAppUi;
import com.android.camera2.host.focus.FocusOverlayManager;
import com.android.camera2.host.ui.CameraAppUi;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static com.android.camera2.common.utils.CameraParameter.FRONT_CAMERA;
import static com.android.camera2.common.utils.CameraParameter.MAIN_CAMERA;


public class PhotoModule implements ICameraMode {

    private static final String TAG = PhotoModule.class.getSimpleName();
    //Executor
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE = 1;
    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
            TimeUnit.SECONDS, sPoolWorkQueue, new ThreadPoolExecutor.DiscardOldestPolicy());

    //App
    private static IApp mApp;
    private Context mContext;
    private boolean isPause = false;
    private boolean isNeedRatio = false;
    private MyOrientationListener orientationListener;

    //Surface
    private Surface mPreviewSurface;
    private ImageReader mCaptureReader;
    private CameraCaptureSession mCaptureSession;
    private Size size;

    private int CURRENT_FLASH_STATE = 0;
    private float CURRENT_RATIO = CameraParameter.RATIO_4_3;
    private CaptureRequest.Builder mPreviewBuilder;

    //Camera
    private int mSensorOrientation;
    private CameraManager mCameraManager;
    private CameraDevice mCameraDevice;
    private int mRotation = 0;
    private CameraCharacteristics characteristics = null;

    //camera id
    private static String CURRENT_CAMERA = "0";

    private static final int STATE_PREVIEW = 0;
    private static final int STATE_WAITING_LOCK = 1;
    private static final int STATE_WAITING_PRE_CAPTURE = 2;
    private static final int STATE_WAITING_NON_PRE_CAPTURE = 3;
    private static final int STATE_PICTURE_TAKEN = 4;
    private int mState = STATE_PREVIEW;


    //focus manager
    private FocusOverlayManager mFocusManager;
    private MeteringRectangle[] mFocusArea;
    private MeteringRectangle[] mMeteringArea;
    private int mLatestAfState = -1;
    private MySeekBar mSeekBar;
    private boolean isLongSingleTap = false;
    // for reset AE/AF metering area
    private MeteringRectangle[] mResetRect = new MeteringRectangle[]{
            new MeteringRectangle(0, 0, 0, 0, 0)
    };

    private boolean isCameraOpened = false;

    public PhotoModule(IApp app) {
        mApp = app;
        mContext = app.getActivity();
    }

    @Override
    public void init() {
        LogUtil.i(TAG, "init...");
        mFocusManager = new FocusOverlayManager(mApp.getAppUi().getFocusView(), mApp.getActivity().getMainLooper(), (CameraAppUi) mApp.getAppUi());
        THREAD_POOL_EXECUTOR.execute(new Runnable() {
            @Override
            public void run() {
                mApp.getAppUi().setPrevSurfaceListener(previewSurfaceCallback);
                mApp.getAppUi().setCameraSwitcherListener(cameraSwitchListener);
                mApp.getAppUi().setShutterListener(mShutterListener);
                mApp.getAppUi().setFlashStateListener(mFlashStateListener);
                mApp.getAppUi().setSingleTapLister(mSingleTapListener);
                mApp.getAppUi().setRatioListener(mRarioListener);
                orientationListener = new MyOrientationListener(mContext);
            }
        });
        mSeekBar = mApp.getActivity().findViewById(R.id.seekbar);
        mSeekBar.setOnSeekBarChangeListener(onSeekBarChangeListener);

        getCameraId();
        openCamera();
        size = findBestPreviewSize(CURRENT_RATIO);
        mApp.getAppUi().setPreviewSize(size);
    }

    @Override
    public void resume() {
        openCamera();
    }

    @Override
    public void pause() {
        isPause = true;
        stopPreview();
//        closeCamera();
        mFocusManager.removeDelayMessage();
        mFocusManager.hideFocusUI();
        mSeekBar.setSeekbarVisibility(View.GONE);
        //此处把预览的surface置为null会导致点击thumbnail后返回无法继续预览
//        mPreviewSurface = null;
    }

    @Override
    public void destroy() {
        LogUtil.i(TAG, "destroy");
        isPause = false;
        isNeedRatio = false;
        mApp.getAppUi().setPrevSurfaceListener(null);
        mApp.getAppUi().setCameraSwitcherListener(null);
        mApp.getAppUi().setShutterListener(null);
        mApp.getAppUi().setFlashStateListener(null);
        mApp.getAppUi().setSingleTapLister(null);
        mApp.getAppUi().setRatioListener(null);
        mSeekBar.setOnSeekBarChangeListener(null);
        closeCamera();
        mApp.releaseBackgroundHandler();

    }

    private SeekBar.OnSeekBarChangeListener onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            LogUtil.i(TAG, "currentProgress = " + progress);

            //range 12   -12  step 1/6
            Range<Integer> r = characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE);
            Rational rational = characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP);
            LogUtil.e(TAG, "up = " + r.getUpper() + "\tlow = " + r.getLower() + "\tRational = " + rational);

            int maxmax = r.getUpper();
            int minmin = r.getLower();
            int all = (-minmin) + maxmax;
            int time = 100 / all;
            int ae = ((progress / time) - maxmax) > maxmax ? maxmax : ((progress / time) - maxmax) < minmin ? minmin : ((progress / time) - maxmax);
            mPreviewBuilder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, ae);
            LogUtil.i(TAG, "iso_min = " + minmin + "\tiso_max = " + maxmax + "\t曝光增益 = " + ae);

            try {
                mCaptureSession.setRepeatingRequest(mPreviewBuilder.build(), null, mApp.getBackgroundHandler());
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            LogUtil.i(TAG, "onStartTrackingTouch----1");
        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            LogUtil.i(TAG, "onStopTrackingTouch----2");
        }
    };

    private SurfaceHolder.Callback previewSurfaceCallback = new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(@NonNull SurfaceHolder holder) {
            LogUtil.i(TAG, "onSurfaceCreate = " + holder);
            if (orientationListener.canDetectOrientation()) {
                orientationListener.enable();
            }
            mPreviewSurface = holder.getSurface();
            LogUtil.d(TAG, "mPreviewSurface_111 = " + mPreviewSurface);
            startPreview();
        }

        @Override
        public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
            LogUtil.i(TAG, "onSurfaceChanged" + "width = " + width + "--height = " + height);
            mPreviewSurface = holder.getSurface();
            mFocusManager.onPreviewChanged(width, height, characteristics);
            if (!isPause && isNeedRatio) {
                stopPreview();
                closeCamera();
                openCamera();
            }
        }

        @Override
        public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
            LogUtil.i(TAG, "onSurfaceDestroyed");
            mPreviewSurface = null;
            orientationListener.disable();
        }
    };

    private IAppUi.CameraSwitchListener cameraSwitchListener = new IAppUi.CameraSwitchListener() {
        @Override
        public void onCameraSwitch() {
            LogUtil.d(TAG, "do onCameraSwitch " + CURRENT_CAMERA);

            if (CURRENT_CAMERA.equals(MAIN_CAMERA)) {
                CURRENT_CAMERA = FRONT_CAMERA;
                mSeekBar.setSeekbarVisibility(View.GONE);
            } else if (CURRENT_CAMERA.equals(FRONT_CAMERA)) {
                CURRENT_CAMERA = MAIN_CAMERA;
            }
            stopPreview();
            closeCamera();
            openCamera();
        }
    };

    private IAppUi.ShutterListener mShutterListener = new IAppUi.ShutterListener() {

        @Override
        public void onShutterLongPressed() {
            LogUtil.i(TAG, "onShutterLongPressed...");
        }

        @Override
        public void onShutterPressed() {
            runCaptureStep();
//            new CountDownTimer(3000, 1000) {
//
//                @Override
//                public void onTick(long millisUntilFinished) {
//                    LogUtil.i(TAG,"onTick --->" + millisUntilFinished/1000);
//                    Toast.makeText(mContext,String.valueOf((millisUntilFinished/1000)),Toast.LENGTH_SHORT).show();
//                }
//
//                @Override
//                public void onFinish() {
//                    LogUtil.i(TAG,"onFinish ...111");
//                    takePic();
//                }
//            }.start();
        }
    };

    private void sendStillPictureRequest() {
        try {
            CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            builder.addTarget(mCaptureReader.getSurface());
            //拍照时自动对焦
            builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
            setFlashState(builder, true);
//            int rotation = mContext.getDisplay().getRotation();
            LogUtil.i(TAG, "screen_orientation = " + mRotation);
            builder.set(CaptureRequest.JPEG_ORIENTATION, mRotation);
//            mCaptureSession.stopRepeating();
//            mCaptureSession.abortCaptures();
            mCaptureSession.capture(builder.build(), new CameraCaptureSession.CaptureCallback() {
                @Override
                public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) {
                    super.onCaptureStarted(session, request, timestamp, frameNumber);
                }

                @Override
                public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                    super.onCaptureCompleted(session, request, result);
                    mApp.getSoundManager().playShutterClickSound();
                    resetTriggerState();
                }
            }, mApp.getBackgroundHandler());
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * Listen for device orientation
     * when the orientation of the device has changed
     */
    private class MyOrientationListener extends OrientationEventListener {

        public MyOrientationListener(Context context) {
            super(context);
        }

        @Override
        public void onOrientationChanged(int orientation) {
            mRotation = (orientation + 45) / 90 * 90;
//            LogUtil.i(TAG, "orientation_change = " + mRotation);
        }
    }

    private IAppUi.FlashStateListener mFlashStateListener = new IAppUi.FlashStateListener() {
        @Override
        public void flashOn() {

            LogUtil.i(TAG, "flash on");
            //turn on flash
            CURRENT_FLASH_STATE = CameraParameter.FLASH_ON;
            setFlashState(mPreviewBuilder, true);
        }

        @Override
        public void flashOff() {

            LogUtil.i(TAG, "flash off");
            //turn off flash
            CURRENT_FLASH_STATE = CameraParameter.FLASH_OFF;
            setFlashState(mPreviewBuilder, true);
        }

        @Override
        public void flashAuto() {
            LogUtil.i(TAG, "flash auto");
            //auto
            CURRENT_FLASH_STATE = CameraParameter.FLASH_AUTO;
            setFlashState(mPreviewBuilder, true);

        }

        @Override
        public void flashTroch() {
            LogUtil.i(TAG, "flash troch");
            CURRENT_FLASH_STATE = CameraParameter.FLASH_TROCH;
            setFlashState(mPreviewBuilder, true);

        }
    };
    private IAppUi.SingleTapListener mSingleTapListener = new IAppUi.SingleTapListener() {
        @Override
        public void onSingleTap(float x, float y) {
//              mFocusManager.hideFocusUI();
            isLongSingleTap = false;
            mSeekBar.setSeekbarVisibility(View.GONE);
            mFocusManager.startFocus(x, y);
            MeteringRectangle focusRect = mFocusManager.getFocusArea(x, y, true);
            MeteringRectangle meterRect = mFocusManager.getFocusArea(x, y, false);
            mPreviewBuilder = getPreviewBuilder();
            mPreviewBuilder.set(CaptureRequest.CONTROL_AE_LOCK, false);
            setFlashState(mPreviewBuilder, false);
            LogUtil.i(TAG, "camera_state" + CURRENT_FLASH_STATE);

            CaptureRequest request = getTouch2FocusRequest(mPreviewBuilder, focusRect, meterRect);
            try {
                mCaptureSession.setRepeatingRequest(request, mPreviewCallback, mApp.getMainHandler());

                // trigger af
                mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
                mCaptureSession.capture(mPreviewBuilder.build(), null, mApp.getMainHandler());
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onLongSingleTap(float x, float y) {
            LogUtil.i(TAG, "event x = " + x + "---event y = " + y);
            mSeekBar.setTranslationX(x - 100);
            mSeekBar.setTranslationY(y - 180);
            mSeekBar.setProgress(50);
            mSeekBar.setSeekbarVisibility(View.VISIBLE);

            isLongSingleTap = true;
            mFocusManager.setFocusUI(x, y);
            MeteringRectangle focusRect = mFocusManager.getFocusArea(x, y, true);
            MeteringRectangle meterRect = mFocusManager.getFocusArea(x, y, false);
            mPreviewBuilder = getPreviewBuilder();
            mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
            mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
            mPreviewBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
            CaptureRequest request = getTouch2FocusRequestForAEAF(mPreviewBuilder, focusRect, meterRect);

            try {
                mCaptureSession.setRepeatingRequest(request, new CameraCaptureSession.CaptureCallback() {
                    @Override
                    public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                        super.onCaptureCompleted(session, request, result);
                        LogUtil.i(TAG, "result = " + result.get(CaptureResult.CONTROL_AF_STATE));
                        int afState = result.get(CaptureResult.CONTROL_AF_STATE);
                        updateAfState(result);
                        if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
                                CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
                            mFocusManager.displayFocusUI();
                            mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
                            try {
                                mCaptureSession.setRepeatingRequest(mPreviewBuilder.build(), null, null);
                            } catch (CameraAccessException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                    @Override
                    public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
                        super.onCaptureProgressed(session, request, partialResult);
//                        int afState = partialResult.get(CaptureResult.CONTROL_AF_STATE);
//                        updateAfState(partialResult);
//                        LogUtil.i(TAG, "onCaptureProgressed---afState = " + afState);
                    }
                }, mApp.getMainHandler());
                mCaptureSession.capture(request, null, mApp.getMainHandler());
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void resetTouchToFocus() {
            sendControlFocusModeRequest(CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
        }
    };

    private IAppUi.RatioListener mRarioListener = new IAppUi.RatioListener() {

        @Override
        public void ratio_1_1() {
            handRatioChanged(CameraParameter.RATIO_1_1);
        }

        @Override
        public void ratio_4_3() {
            handRatioChanged(CameraParameter.RATIO_4_3);
        }

        @Override
        public void ratio_16_9() {
            handRatioChanged(CameraParameter.RATIO_16_9);
        }

        @Override
        public void ratio_full() {
            handRatioChanged(CameraParameter.RATIO_full);
        }
    };

    private void handRatioChanged(float ratio11) {
//        stopPreview();
//        closeCamera();
        CURRENT_RATIO = ratio11;
        size = findBestPreviewSize(CURRENT_RATIO);
        mApp.getAppUi().setPreviewSize(size);
        isNeedRatio = true;
    }

    private CaptureRequest getTouch2FocusRequest(CaptureRequest.Builder builder, MeteringRectangle focus, MeteringRectangle metering) {
        int afMode = getValidAFMode(CaptureRequest.CONTROL_AF_MODE_AUTO);
        builder.set(CaptureRequest.CONTROL_AF_MODE, afMode);
        builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
        if (mFocusArea == null) {
            mFocusArea = new MeteringRectangle[]{focus};
        } else {
            mFocusArea[0] = focus;
        }
        if (mMeteringArea == null) {
            mMeteringArea = new MeteringRectangle[]{metering};
        } else {
            mMeteringArea[0] = metering;
        }
        if (isMeteringSupport(true)) {
            builder.set(CaptureRequest.CONTROL_AF_REGIONS, mFocusArea);
        }
        if (isMeteringSupport(false)) {
            builder.set(CaptureRequest.CONTROL_AE_REGIONS, mMeteringArea);
        }
        builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
        return builder.build();
    }

    private CaptureRequest getTouch2FocusRequestForAEAF(CaptureRequest.Builder builder, MeteringRectangle focus, MeteringRectangle metering) {
        int afMode = getValidAFMode(CaptureRequest.CONTROL_AF_MODE_AUTO);
        builder.set(CaptureRequest.CONTROL_AF_MODE, afMode);
        builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
        if (mFocusArea == null) {
            mFocusArea = new MeteringRectangle[]{focus};
        } else {
            mFocusArea[0] = focus;
        }
        if (mMeteringArea == null) {
            mMeteringArea = new MeteringRectangle[]{metering};
        } else {
            mMeteringArea[0] = metering;
        }
        if (isMeteringSupport(true)) {
            builder.set(CaptureRequest.CONTROL_AF_REGIONS, mFocusArea);
        }
        if (isMeteringSupport(false)) {
            builder.set(CaptureRequest.CONTROL_AE_REGIONS, mMeteringArea);
        }
        return builder.build();
    }

    private void sendControlFocusModeRequest(int focusMode) {
        LogUtil.i(TAG, "focusMode:" + focusMode);
        CaptureRequest request = getFocusModeRequest(getPreviewBuilder(), focusMode);
        try {
            mCaptureSession.setRepeatingRequest(request, mPreviewCallback, mApp.getMainHandler());
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private int getValidAFMode(int targetMode) {
        int[] allAFMode = characteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
        for (int mode : allAFMode) {
            if (mode == targetMode) {
                return targetMode;
            }
        }
        Log.i(TAG, "not support af mode:" + targetMode + " use mode:" + allAFMode[0]);
        return allAFMode[0];
    }

    private boolean isMeteringSupport(boolean focusArea) {
        int regionNum;
        if (focusArea) {
            regionNum = characteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
        } else {
            regionNum = characteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE);
        }
        return regionNum > 0;
    }

    private CaptureRequest.Builder getPreviewBuilder() {
        if (mPreviewBuilder == null) {
            try {
                mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
        return mPreviewBuilder;
    }


    @SuppressLint("MissingPermission")
    @Override
    public void openCamera() {
//        size = findBestPreviewSize(CURRENT_RATIO);
//        mApp.getAppUi().setPreviewSize(size);
        try {
            mCameraManager.openCamera(CURRENT_CAMERA, cameraDeviceCallback, mApp.getBackgroundHandler());
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void startPreview() {
        LogUtil.i(TAG, "mPreviewSurface = " + mPreviewSurface + "\nmPreviewBuilder = " + mPreviewBuilder + "\nmCameraDevice = " + mCameraDevice);
        if (!isCameraOpened || mPreviewSurface == null)
            return;

//        Toast.makeText(mContext, "photo mode is opened", Toast.LENGTH_SHORT).show();
        try {
            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            //设置自动对焦
            mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            //曝光补偿
            mPreviewBuilder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0);

            setFlashState(mPreviewBuilder, false);
            mPreviewBuilder.addTarget(mPreviewSurface);
            mCameraDevice.createCaptureSession(Arrays.asList(mPreviewSurface, mCaptureReader.getSurface()), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession session) {
                    mCaptureSession = session;
                    try {
                        session.setRepeatingRequest(mPreviewBuilder.build(), mPreviewCallback, mApp.getBackgroundHandler());
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                    LogUtil.e(TAG, "[onConfigureFailed]……" + session);
                    stopPreview();
                    closeCamera();
                    openCamera();
                }
            }, mApp.getBackgroundHandler());
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private CameraCaptureSession.CaptureCallback mPreviewCallback = new CameraCaptureSession.CaptureCallback() {

        @Override
        public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
            super.onCaptureProgressed(session, request, partialResult);
            updateAfState(partialResult);
            processPreCapture(partialResult);

        }

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
            super.onCaptureCompleted(session, request, result);
            updateAfState(result);
            processPreCapture(result);
        }

    };


    private void updateAfState(CaptureResult result) {
        Integer state = result.get(CaptureResult.CONTROL_AF_STATE);
        if (state != null && mLatestAfState != state) {
            mLatestAfState = state;
            mApp.getActivity().runOnUiThread(new AFStateChangedRunable(state, mFocusManager));
        }
    }

    private class AFStateChangedRunable implements Runnable {
        final Integer mState;
        FocusOverlayManager focusOverlayManager;

        private AFStateChangedRunable(Integer state, FocusOverlayManager manager) {
            mState = state;
            focusOverlayManager = manager;
        }

        @Override
        public void run() {
            onAFStateChanged(mState, focusOverlayManager);
        }
    }

    private void onAFStateChanged(Integer afState, FocusOverlayManager manager) {
        LogUtil.i(TAG, "afState = " + afState);
        switch (afState) {
            case CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN:
                LogUtil.i(TAG, "af_state -----------> startFocus ");
                manager.startFocus(isLongSingleTap);
                break;
            case CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED:
            case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED:
                LogUtil.i(TAG, "af_state -----------> focusSuccess ");
                manager.focusSuccess();
//                mFocusManager.hideFocusUI();
                LogUtil.i(TAG, "mState = " + mState);
                break;
            case CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN:
                LogUtil.i(TAG, "af_state -----------> autoFocus ");
                manager.autoFocus();
                break;
            case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
            case CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED:
                LogUtil.i(TAG, "af_state -----------> focusFailed ");
                manager.focusFailed();
                break;
            case CaptureResult.CONTROL_AF_STATE_INACTIVE:
                LogUtil.i(TAG, "af_state -----------> hideFocusUI ");
                manager.hideFocusUI();
                break;
        }
    }


    private void setFlashState(CaptureRequest.Builder previewBuilder, Boolean isFlashIconTriger) {
        if (CURRENT_FLASH_STATE == CameraParameter.FLASH_OFF) {
            previewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
            previewBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);

        } else if (CURRENT_FLASH_STATE == CameraParameter.FLASH_ON) {
            previewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
            previewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
        } else if (CURRENT_FLASH_STATE == CameraParameter.FLASH_AUTO) {
            previewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
            previewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
            previewBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
        } else if (CURRENT_FLASH_STATE == CameraParameter.FLASH_TROCH) {
            previewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
            previewBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
        }

        if (isFlashIconTriger) {
            try {
                mCaptureSession.setRepeatingRequest(mPreviewBuilder.build(), mPreviewCallback, mApp.getBackgroundHandler());
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

    }

    @Override
    public void stopPreview() {
        LogUtil.i(TAG, "stopPreview");
        if (mCaptureSession != null) {
            try {
                mCaptureSession.abortCaptures();
                mCaptureSession.close();
//                mCaptureSession = null;
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void closeCamera() {
        LogUtil.i(TAG, "closeCamera");
        if (null != mCaptureSession) {
            mCaptureSession.close();
            mCaptureSession = null;
        }
        if (null != mCameraDevice) {
            mCameraDevice = null;
        }
        isCameraOpened = false;
    }


    //Camera device state callback
    private CameraDevice.StateCallback cameraDeviceCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice camera) {
            LogUtil.i(TAG, "camera device state :  onOpened");
            isCameraOpened = true;
            mCameraDevice = camera;
            startPreview();
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
            LogUtil.i(TAG, "camera device state :  onDisconnected");
            if (mCameraDevice != null) {
                mCameraDevice.close();
                mCameraDevice = null;
            }
            isCameraOpened = false;
        }

        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
            LogUtil.i(TAG, "camera device state :  onError");
            if (mCameraDevice != null) {
                mCameraDevice.close();
                mCameraDevice = null;
            }
            isCameraOpened = false;
        }
    };

    private void getCameraId() {
        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
        try {
            String[] cameraIds = mCameraManager.getCameraIdList();
            for (String id : cameraIds) {
                Log.e(TAG, "getCameraId: " + id);
            }
            for (String cameraid : cameraIds) {
                CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(cameraid);
                //skip the front camera device
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
                    continue;
                }
                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                if (map == null) {
                    continue;
                }
                CURRENT_CAMERA = cameraid;
                mSensorOrientation = Integer.parseInt(characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION).toString());
                int windowOrientation = mApp.getActivity().getWindowManager().getDefaultDisplay().getOrientation();
                LogUtil.i(TAG, "mSensorOrientation = " + mSensorOrientation
                        + "\nwindowOrientation = " + windowOrientation);
                break;
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }


    private Size findBestPreviewSize(float ratio) {
        //configure surfaces

        int mPreviewWidth = 0;
        int mPreviewHeight = 0;
        try {
            characteristics = mCameraManager.getCameraCharacteristics(CURRENT_CAMERA);

        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        if (characteristics != null) {
            StreamConfigurationMap streamMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            Size[] supportSizes = streamMap.getOutputSizes(ImageFormat.JPEG);

            float tolerant = 0.05f;
            int fullScreenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
            int fullScreenHeight = mContext.getResources().getDisplayMetrics().heightPixels;
            LogUtil.i(TAG, "findBestPreviewSize : fullScreenWidth = " + fullScreenWidth + " fullScreenHeight = " + fullScreenHeight);
            Size bestCaptureSize = new Size((int) (fullScreenWidth * ratio), fullScreenWidth);
            float bestRatio = ratio;
            for (Size size : supportSizes) {
                if (Math.abs((float) size.getWidth() / size.getHeight() - ratio) < tolerant) {
                    tolerant = Math.abs((float) size.getWidth() / size.getHeight() - ratio);
                    bestCaptureSize = size;
                    bestRatio = (float) size.getWidth() / size.getHeight();
                }
            }
            LogUtil.i(TAG, "bestCaptureSize = " + bestCaptureSize);
            //capture surface
            int mCaptureWidth = bestCaptureSize.getWidth();
            int mCaptureHeight = bestCaptureSize.getHeight();
            //init imageReader
            mCaptureReader = ImageReader.newInstance(mCaptureWidth, mCaptureHeight, ImageFormat.JPEG, 1);
            mCaptureReader.setOnImageAvailableListener(onImageAvailableListener, mApp.getBackgroundHandler());

            //preview surface
            int previewHeight = fullScreenWidth - fullScreenWidth % (16 * 3);
            int previewWidth = (int) (previewHeight * ratio);
            if (previewWidth != mPreviewWidth || previewHeight != mPreviewHeight) {
                mPreviewHeight = previewHeight;
                mPreviewWidth = previewWidth;
            }
            if (ratio == CameraParameter.RATIO_full) {
                mPreviewWidth = fullScreenHeight;
                mPreviewHeight = fullScreenWidth;
            }
        }
        return new Size(mPreviewWidth, mPreviewHeight);
    }


    private ImageReader.OnImageAvailableListener onImageAvailableListener = new ImageReader.OnImageAvailableListener() {
        @Override
        public void onImageAvailable(ImageReader reader) {
            Image image = reader.acquireNextImage();
            THREAD_POOL_EXECUTOR.execute(new ImageSaver(image));

        }
    };

    private class ImageSaver implements Runnable {
        private Image mImage;

        public ImageSaver(Image image) {
            mImage = image;
        }

        @Override
        public void run() {
            ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            mImage.close();
            Bitmap adjustBitmap;
            adjustBitmap = CameraUtils.adjustSourcePic(CURRENT_CAMERA, bytes);
            CameraUtils.saveBitmap(adjustBitmap);
            mApp.getAppUi().displayThumbnail(adjustBitmap);
        }
    }


    public CaptureRequest getFocusModeRequest(CaptureRequest.Builder builder, int focusMode) {
        int afMode = getValidAFMode(focusMode);
        builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
        builder.set(CaptureRequest.CONTROL_AF_MODE, afMode);
        builder.set(CaptureRequest.CONTROL_AF_REGIONS, mResetRect);
        builder.set(CaptureRequest.CONTROL_AE_REGIONS, mResetRect);
        // cancel af trigger
        builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
        return builder.build();
    }


    private void processPreCapture(CaptureResult result) {
        switch (mState) {
            case STATE_PREVIEW: {
                // We have nothing to do when the camera preview is working normally.
                break;
            }
            case STATE_WAITING_LOCK: {
                Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
                if (afState == null) {
                    sendStillPictureRequest();
                } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
                        CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
                    LogUtil.i(TAG, "afState_1 = " + afState);
//                     CONTROL_AE_STATE can be null on some devices
                    Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                    if (aeState == null ||
                            aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
                        mState = STATE_PICTURE_TAKEN;
                        sendStillPictureRequest();
                    } else {
                        triggerAECaptureSequence();
                    }
                }
                break;
            }
            case STATE_WAITING_PRE_CAPTURE: {
                // CONTROL_AE_STATE can be null on some devices
                Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                if (aeState == null ||
                        aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
                        aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
                    mState = STATE_WAITING_NON_PRE_CAPTURE;
                }
                break;
            }
            case STATE_WAITING_NON_PRE_CAPTURE: {
                // CONTROL_AE_STATE can be null on some devices
                Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
                    mState = STATE_PICTURE_TAKEN;
                    sendStillPictureRequest();
                }
                break;
            }
        }
    }

    private void resetTriggerState() {
        mState = STATE_PREVIEW;
        CaptureRequest.Builder builder = getPreviewBuilder();
        builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
        builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
                CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
        try {
            mCaptureSession.setRepeatingRequest(builder.build(), mPreviewCallback, mApp.getMainHandler());
            mCaptureSession.capture(builder.build(), mPreviewCallback, mApp.getMainHandler());
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void runCaptureStep() {
        if (canTriggerAf() && !isLongSingleTap) {
            triggerAFCaptureSequence();
        } else {
            sendStillPictureRequest();
        }
    }

    private void triggerAECaptureSequence() {
        CaptureRequest.Builder builder = getPreviewBuilder();
        builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
                CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
        mState = STATE_WAITING_PRE_CAPTURE;
        try {
            mCaptureSession.capture(builder.build(), mPreviewCallback, mApp.getMainHandler());
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void triggerAFCaptureSequence() {
        CaptureRequest.Builder builder = getPreviewBuilder();
        builder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                CaptureRequest.CONTROL_AF_TRIGGER_START);
        mState = STATE_WAITING_LOCK;
        try {
            mCaptureSession.capture(builder.build(), mPreviewCallback, mApp.getMainHandler());
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * is auto focus Trigger
     *
     * @return return true if af triggered
     */
    boolean canTriggerAf() {
        int[] allAFMode = characteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
        return allAFMode != null && allAFMode.length > 1;
    }


}
